import pandas as pd
import numpy as np
import os
from tkinter import messagebox
from data.Database import Subject
from interface import Interface


# 该类为分析的核心函数，具体执行各种功能，分析算法请参考interface类
class Core(Interface):
    def __init__(self, path: str, ana: dict):
        super().__init__()
        self.path = path
        self.check = False
        self.ana = ana
        self.subject = dict()
        self.grade_analysis = None
        self.subject_with_mc = list()
        if not os.path.exists(path):
            messagebox.showerror('文件错误！', '文件未选择或者文件路径不存在！')
            return
        else:
            self.df = pd.read_excel(path, sheet_name=0, header=0)
            # 检查班级和姓名是否存在
            if not ({'bj', 'xm'} < set(self.df.columns)):
                messagebox.showerror('缺失列！', "请将姓名和班级加入表格，并将标题设置为xm和bj！")
                return
            self.remove_zero()
            self.read_subject()
            self.start_procedure()
            self.check = True

    # 读取科目的配置信息， 同时进行排名计算
    # 需要用于配置的科目信息  科目名称/简称 总分
    def read_subject(self):
        model = Subject()
        subjects = model.all()
        for item in subjects:
            if item.jc in self.df.columns:
                self.df[item.jc + 'mc'] = self.df[item.jc].rank(ascending=False, method='min')
                self.subject[item.jc] = item
                self.subject_with_mc += [item.jc, item.jc+'mc']

        # 检查识别的列
        if len(self.subject) == 0:
            messagebox.showerror('列名出错！', "未识别出可以分析的列，请检查excel列命名是否是科目简称！")
            return

        # 添加总分列
        if 'zf' not in self.subject:
            mf = sum([item.mf for key, item in self.subject.items()])
            zf = Subject().instance(id=100, title='总分', jc='zf', mf=mf, state=False)
            self.df['zf'] = self.df[self.subject.keys()].sum(axis=1)
            self.df['zfmc'] = self.df['zf'].rank(ascending=False, method='min')
            self.df['zfmc'] = self.df['zfmc'].apply(int)
            self.subject['zf'] = zf
            self.subject_with_mc += ['zf', 'zfmc']

    # 移除 0 分数据
    def remove_zero(self):
        if self.ana.get('zero').get():
            self.df.replace({0: np.nan}, inplace=True)

    def start_procedure(self):
        self.weight_re_score()

    # 将导入的数据添加名次后返回
    def basic_data(self):
        return self.translate(self.df.copy())

    # 成绩总体分布计算函数，计算分数分布情况，考虑到学校人少，仅分成10个段
    def distribution(self):
        setting = self.ana.get('score_cut').get()
        if setting:
            bins1 = range(0, 151, setting)
        else:
            bins1 = 5
        # index = ['name', '10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%']
        list_all = []
        # 重新划分各段的分数
        for subject in self.subject.keys():
            # score = self.get_mf(subject)
            # if score:
            #     bins = range(0, score+1, score // 10)
            # else:
            #     bins = bins1
            bins = bins1
            # 统计各段人数
            # print(bins, self.df)
            counts = pd.cut(self.df[subject], bins=bins).value_counts(sort=False)
            # print(counts)
            # 生成科目分段的series， 并添加到列表中
            # ser = pd.Series([subject] + list(counts), index=index)
            ser = pd.Series([subject] + list(counts), index=['学科']+list(counts.index))
            list_all.append(ser)
        # 结果生成dataFrame
        df = pd.DataFrame(list_all)
        df.set_index('学科', inplace=True)
        return self.translate(df)

    # 年级统计，因为系统需要使用该数据，所以会有两个函数分别用于使用和导出
    @property
    def grade_status(self):
        if self.grade_analysis is None:
            df = self.df[self.subject.keys()]
            self.grade_analysis = df.agg(['count', 'max', 'min', 'mean', 'std', self.excellent_rate, self.passing_rate])
        # print(self.grade_analysis)
        return self.grade_analysis

    # 年级总体情况分析
    def summarize_grade(self):
        return self.translate(self.grade_status)

    # 分班级学科统计
    def summarize_class(self):
        # 根据用户选择分别设置需要统计的方式
        analysis1, analysis2 = self.ana_setting()
        # 名次列和学科列的统计方式不同，因此需要根据其特点分别应用不同的统计方式
        # analysis1为学科列统计， 2为名次列统计
        df = self.df[['bj'] + self.subject_with_mc]  # 重新采样数据
        # 分班统计
        df_g = df.groupby('bj')
        # df.agg({'yw': ['max', 'min']})可以配置不同列的统计方式
        # 每个列对应的分析方式的设置
        analysis_param = {}  # 分析对应的字典
        for subject in self.subject_with_mc:
            if subject.endswith('mc'):
                analysis_param[subject] = analysis2
            else:
                analysis_param[subject] = analysis1
        # print('ANALYSIS_PARAM', analysis_param)
        df_analysis = df_g.agg(analysis_param)
        # 计算有效达标
        # 计算的逻辑为：先选出总分达标的同学，然后再分班级统计学科达标的同学，因为已经提前拥有该列，所以可以直接更改
        real_person = self.ana.get('task').get()
        if self.ana.get('inclusion_single').get():
            df_g2 = df[df['zfmc'] < int(real_person)].groupby('bj')  # 总分达标
            analysis_param_2 = {}
            for subject in self.subject_with_mc:
                if subject.endswith('mc'):
                    analysis_param_2[subject] = analysis2
            df_analysis_valid = df_g2.agg(analysis_param_2)
            # print(df_analysis_valid['ywmc'])
            # 将结果更新到统计结果中
            for subject in self.subject_with_mc:
                if subject.endswith('mc'):
                    df_analysis[(subject, 'task_valid')] = df_analysis_valid[subject]['task']
                    df_analysis[(subject, 'task_valid')].fillna(0, inplace=True)
                    df_analysis[(subject, 'task_valid')] = df_analysis[(subject, 'task_valid')].apply(int)
        # 是否计算得分
        if self.ana.get('rsr').get():
            df_analysis = pd.concat([df_analysis, self.score(df_analysis)], axis=1)
        return self.translate(df_analysis)

















